﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using Ppt = Microsoft.Office.Interop.PowerPoint;
using Microsoft.Office.Core;

namespace DataFactory
{
    // Note: the methods that handle deserialization are static whilst the methods that handle
    // serialization are non-static. This is because the serialization methods refer to a single
    // XmlPart Id assigned abitrarily by VSTO and having the ability to instantiate the class
    // any number of times allows for controlled serialization to different XmlParts.
	public class PPTSerializer
	{
		string _partId;

        public void SerializeToPresentation(Ppt.Presentation pres, object o, string xmlRootName = null)
        {
            string xmlString = SerializeObject(o, xmlRootName);
            SerializeToPresentation(pres, xmlString);
        }

        public void SerializeToPresentation(Ppt.Presentation pres, string xmlString)
        {
            if (_partId == null)
                _partId = AddXmlPart(pres, xmlString);
            else
            {
                pres.CustomXMLParts.SelectByID(_partId).Delete();
                _partId = AddXmlPart(pres, xmlString);
            }
        }

		public static String SerializeObject(Object o, string xmlRootName = null)
        {   
            using (MemoryStream stream = new MemoryStream())
            {
                XmlSerializer serializer = xmlRootName == null ?
                    new XmlSerializer(o.GetType()) :
                    new XmlSerializer(o.GetType(), new XmlRootAttribute(xmlRootName));
                serializer.Serialize(stream, o);

                return System.Text.Encoding.UTF8.GetString(stream.ToArray());
            }
        }

        // Experimentation using DataContractSerializer to serialize a Dictionary.
        public void SerializeDictionaryToPresentation(Ppt.Presentation pres, object o)
        {
            string xmlString = SerializeDictionary(o);
            SerializeToPresentation(pres, xmlString);
        }

        // Experimentation using DataContractSerializer to serialize a Dictionary.
        public string SerializeDictionary(object o)
        {
            using (MemoryStream stream = new MemoryStream())
            //using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream))
            {
                DataContractSerializer serializer = new DataContractSerializer(o.GetType());
                //serializer.WriteObject(writer, o);
                serializer.WriteObject(stream, o);

                return System.Text.Encoding.UTF8.GetString(stream.ToArray());
            }
        }

        string AddXmlPart(Ppt.Presentation pres, string xmlString)
        {
            CustomXMLPart xmlPart = pres.CustomXMLParts.Add(xmlString);
            return xmlPart.Id;
        }

        public static T Deserialize<T>(Ppt.Presentation pres, string xmlRootName)
        {
            CustomXMLPart xmlPart = GetXMLPart(pres, xmlRootName);
            return xmlPart == null ? default(T) : DeserializeObject<T>(xmlPart.XML);
        }

        public static List<T> DeserializeAll<T>(Ppt.Presentation pres, string xmlRootName)
        {
            IEnumerable<CustomXMLPart> xmlParts = GetAllXMLParts(pres, xmlRootName);
            List<T> objects = new List<T>();

            if (xmlParts != null)
            {
                foreach (var customXmlPart in xmlParts)
                {
                    string xmlString = customXmlPart.XML;
                    objects.Add(DeserializeObject<T>(xmlString));
                }
            }

            return objects;
        }

        public static T DeserializeObject<T>(string xmlString)
        {
            Byte[] bytes = System.Text.Encoding.UTF8.GetBytes(xmlString);

            using (MemoryStream stream = new MemoryStream(bytes))
            {
                XmlSerializer deserializer = new XmlSerializer(typeof(T));
                return (T)deserializer.Deserialize(stream);
            }
        }

        static IEnumerable<CustomXMLPart> GetAllXMLParts(Ppt.Presentation pres, string xmlRootName)
        {
            return pres.CustomXMLParts.Cast<CustomXMLPart>().Where(part => part.DocumentElement.BaseName == xmlRootName).ToList();
        }

        static CustomXMLPart GetXMLPart(Ppt.Presentation pres, string xmlRootName)
        {
            return pres.CustomXMLParts.Cast<CustomXMLPart>().FirstOrDefault(part => part.DocumentElement.BaseName == xmlRootName);
        }

        public static bool ContainsXMLParts(Ppt.Presentation pres, string xmlRootName)
        {
            return pres.CustomXMLParts.Cast<CustomXMLPart>().Any(part => part.DocumentElement.BaseName == xmlRootName);
        }
	}
}
